//
// Created by hsj on 2024-1-10.
// 编译：gcc oob_recv_win.c -LH:\mingw\mingw64\x86_64-w64-mingw32\lib -lws2_32 -o oob_recv_win
//
// --------------------------------------
// | URG=1, URG指针=3 | 8 | 9 | 0 |  . |  .
// --------------------------------------
//                                 /|\
//                                  |
//                               URG指针3（紧急消息的下一位3，下标从0开始）
// 如上所述，除紧急指针的前面1个字节外（0），数据接收方将通过调用常用输人函数读取剩余部分。
// 换言之，紧急消息的意义在于督促消息处理，而非紧急传输形式受限的消息。
/**
 * 某次输出：
 * I:\c_network\c_network>oob_recv_win
    oob serve win listen on port:7777
    123
    Urgent message: 4
    567
    Urgent message: 0
    89
 */

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#define BUF_SIZE 30
void ErrorHandling(char *message);

int main(int argc, char *argv[]){
    WSADATA wsaData;
    SOCKET hAcptSock, hRecvSock;
    SOCKADDR_IN recvAdr;
    SOCKADDR_IN sendAdr;
    int sendAdrSize, strLen;
    char buf[BUF_SIZE] ;
    char port[]="7777";
    int fdNum;
    fd_set read, except, readCopy, exceptCopy;
    struct timeval timeout;

    if(WSAStartup(MAKEWORD(2, 2),&wsaData)!=0)
        ErrorHandling( "WSAStartup() error!");

    hAcptSock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&recvAdr, 0, sizeof(recvAdr));
    recvAdr.sin_family=AF_INET;
    recvAdr.sin_addr.s_addr=htonl (INADDR_ANY);
    recvAdr.sin_port=htons(atoi(/*argv[1]*/port));

    if(bind(hAcptSock, (SOCKADDR* )&recvAdr, sizeof(recvAdr) )==SOCKET_ERROR)
        ErrorHandling("bind() error");
    if(listen(hAcptSock, 128)==SOCKET_ERROR)
        ErrorHandling("listen() error");
    printf("oob serve win listen on port:%s\n",port);

    sendAdrSize=sizeof(sendAdr);
    hRecvSock=accept(hAcptSock, (SOCKADDR*)&sendAdr, &sendAdrSize);

    FD_ZERO(&read);
    FD_ZERO(&except);
    FD_SET(hRecvSock, &read);
    FD_SET(hRecvSock, &except);

    while (1){
        readCopy=read;
        exceptCopy=except;

        timeout.tv_sec=5;
        timeout.tv_usec=0;
        fdNum=select(0, &readCopy, 0, &exceptCopy, &timeout);//平台select函数的第一个参数是为了保持与(包括Linux的) UNIX系列操作系统的兼容性而添加的，并没有特殊意义。

        if(fdNum==SOCKET_ERROR){
            printf("error：%d, end\n",fdNum);
            break;//异常返回-1，结束循环
        }

        if(fdNum==0)//超时返回0，一个也没有
        {
            printf("timeout,not any client...\n");//超时返回，一个也没有
            continue;
        }

        if(FD_ISSET(hRecvSock, &exceptCopy))//FD_ISSET(int fd, fd_set * fdset) :若参数fdset指向的变量中包含文件描述符fd的信息，则返回“真”。
        {
            strLen = recv(hRecvSock, buf, BUF_SIZE - 1, MSG_OOB);//只关心紧急消息：MSG_OOB
            buf[strLen] = 0;
            printf("Urgent message: %s \n", buf);
        }
        if(FD_ISSET(hRecvSock, &readCopy)){
            strLen=recv(hRecvSock, buf, BUF_SIZE-1, 0);
            if(strLen<=0)
            {
                closesocket(hRecvSock);
                break;
            }
            else
            {
                buf[strLen]=0;
                puts(buf);
            }
        }
    }
    closesocket(hAcptSock);
    WSACleanup( );

    return EXIT_SUCCESS;
}

void ErrorHandling( char *message){
    fputs (message, stderr);
    fputc('\n', stderr);
    exit(1);
}